#!/usr/bin/perl -w
use strict;
use IO::File;

my $address = shift @ARGV; 
if (! defined $address) { die "must supply an address" } 
my @files = @ARGV;
if ($#files < 0) { die "must supply at least one filename" }

my @bytearray = (0x00, 0x00, $address, 0x00);		# first four bytes, all reserved but address

foreach my $file (@files) {
	my $progbytes = compilefile($file);
	push(@bytearray, @{$progbytes});
	
}

print "---EEPROM BYTES WRITTEN---------\n";
foreach my $code (@bytearray) {
	printf "0x%02x, ", $code;
}
print "\n";
my $eefile = new IO::File("> eeprom-data.bin");
print $eefile pack("C*", @bytearray);
close $eefile;

sub compilefile {

	my $filename = shift || die "must supply filename";

	my $fh = new IO::File("< $filename");

	my %labels;		# key label name, value, address of item
	my $curaddr = 0;		# tracking of current address in program
	my %opcodes;		# key is current address, value is complex opcode bundle
			# .. sort of an intermediate representation
	my @program;		# where the program is eventually 'compiled' to

	my %opcodetable = (
	JMP => { baseop => 0xc0, numargs => 1, bytewidth => 1,
		generate => sub { 
			if (exists $labels{$_[1]}) {
				return ($_[0] + $labels{$_[1]});
			} else {
				die "unknown label $_[1]";
			}
		}
	},
	SR => { baseop => 0xb0, numargs => 2, bytewidth => 2,
		generate => sub { return($_[0] + $_[1], $_[2] ); } },
	RRCOPY0 => { baseop => 0xa0, numargs => 1, bytewidth => 1,
		generate => sub { return($_[0] + $_[1] ); } },
	ICRGBI  => { baseop => 0x90, numargs => 1, bytewidth => 1,
		generate => sub { return($_[0] + $_[1] ); } },
	RSVD1 => { baseop => 0x80, numargs => 1, bytewidth => 1 },		# reserved
	IDLY => { baseop => 0x7F, numargs => 2, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2]); } },
	DLY16 => { baseop => 0x7E, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	DLY => { baseop => 0x7D, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	DLY8 => { baseop => 0x7C, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	RED => { baseop => 0x7B, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	GREEN => { baseop => 0x7A, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	BLUE => { baseop => 0x79, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	BRIGHT => { baseop => 0x78, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	# we put I before RGB in bytecode, to optimize fallthrough cases
	RGBI => { baseop => 0x77, numargs => 4, bytewidth => 5,
		generate => sub { return ($_[0], $_[4], $_[1], $_[2], $_[3]); } },
	RGB => { baseop => 0x76, numargs => 3, bytewidth => 4,
		generate => sub { return ($_[0], $_[1], $_[2], $_[3]); } },
	IRED => { baseop => 0x75, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	IGREEN => { baseop => 0x74, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	IBLUE => { baseop => 0x73, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	IBRIGHT => { baseop => 0x72, numargs => 1, bytewidth => 2,
		generate => sub { return ($_[0], $_[1]); } },
	CRGB => { baseop => 0x71, numargs => 1, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] << 4) + ($_[2] << 2) + ($_[3])); } },
	CRGBI => { baseop => 0x70, numargs => 4, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] << 6) + ($_[2] << 4) + ($_[3] << 2) + ($_[4])); } },
# DONE TO HERE
	CDRGB => { baseop => 0x6F, numargs => 2, bytewidth => 3 },
	CDRGBI => { baseop => 0x6E, numargs => 2, bytewidth => 3 },
	ICRGB => { baseop => 0x6D, numargs => 1, bytewidth => 2 },
	# this moved to compressed ops
	xxICRGBI => { baseop => 0x6C, numargs => 1, bytewidth => 2 },
	ICDRGB => { baseop => 0x6B, numargs => 2, bytewidth => 3 },
	ICDRGBI => { baseop => 0x6A, numargs => 2, bytewidth => 3 },
	CMPRR => { baseop => 0x69, numargs => 1, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2]); } },
	xxCMPAR => { baseop => 0x68, numargs => 1, bytewidth => 2 },
	BCC => { baseop => 0x67, numargs => 1, bytewidth => 2 },
	BCS => { baseop => 0x66, numargs => 1, bytewidth => 2 },
	BEQ => { baseop => 0x65, numargs => 1, bytewidth => 2,
		generate => sub { 
			if (exists $labels{$_[1]}) {
				return ($_[0], $labels{$_[1]});
			} else {
				die "unknown label $_[1]";
			}
		}
	},
	BMI => { baseop => 0x64, numargs => 1, bytewidth => 2 },
	BNE => { baseop => 0x63, numargs => 1, bytewidth => 2,
		generate => sub { 
			if (exists $labels{$_[1]}) {
				return ($_[0], $labels{$_[1]});
			} else {
				die "unknown label $_[1]";
			}
		}
	},
	BPL => { baseop => 0x62, numargs => 1, bytewidth => 2 },
	BVC => { baseop => 0x61, numargs => 1, bytewidth => 2 },
	BVS => { baseop => 0x60, numargs => 1, bytewidth => 2 },
	RND => { baseop => 0x5F, numargs => 0, bytewidth => 1, 
		generate => sub { return ($_[0]); } },
	ZEROA => { baseop => 0x5E, numargs => 0, bytewidth => 1 },
	INCA => { baseop => 0x5D, numargs => 0, bytewidth => 1 },
	DECA => { baseop => 0x5C, numargs => 0, bytewidth => 1 },
	INCR => { baseop => 0x5B, numargs => 1, bytewidth => 2 },
	DECR => { baseop => 0x5A, numargs => 1, bytewidth => 2 },
	RRCOPY => { baseop => 0x59, numargs => 2, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2]); } },
	RRSWAP => { baseop => 0x58, numargs => 2, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2]); } },
	IRGBI => { baseop => 0x57, numargs => 4, bytewidth => 3,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2], $_[3] * 16 + $_[4]  ); } },
	IRGB => { baseop => 0x56, numargs => 3, bytewidth => 3,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2], $_[3] * 16  ); } },
	STEPTO => { baseop => 0x55, numargs => 2, bytewidth => 2,
		generate => sub { return($_[0], ($_[1] * 16) + $_[2]); } },
	STEPTOBY => { baseop => 0x54, numargs => 3, bytewidth => 3 },
	DEEPSLEEP => { baseop => 0x02, numargs => 0, bytewidth => 0,
		generate => sub { return ($_[0]); } },
	NOP => { baseop => 0x02, numargs => 0, bytewidth => 0,
		generate => sub { return ($_[0]); } },
	EOP => { baseop => 0x02, numargs => 0, bytewidth => 0,
		generate => sub { return ($_[0]); } }
	);

	while(<$fh>) {
		chomp;
		s/#.*$//;
		s/^\s*(.*?)\s+$/$1/;
		next if (/^$/);
	
		# parse...
		if(/^(\w+):$/) {
			$labels{$1} = $curaddr
		} elsif(/^(\w+)\s*(.*)$/) {
			if(exists $opcodetable{$1}) {
				my $opcode = $1;
				my @params = split(",", $2);
				if ($opcodetable{$opcode}{numargs} == ($#params + 1)) {
					# stick into intermediate representation:
					$opcodes{$curaddr} = ();
					$opcodes{$curaddr}{op} = $opcode;
					$opcodes{$curaddr}{params} = \@params;
	# op => $opcode);
					#if(@params) {
						$opcodes{$curaddr}{params} = \@params;
					#}
	
					# push code pointer forward
					$curaddr += $opcodetable{$opcode}{bytewidth}; 
				} else {
					die "expected $opcodetable{$1}{numargs} params to $opcode, got " . ($#params + 1);
				}
			} else {
				die "unknown opcode $1 in $_";
			}
		} else {
			die "syntax error: $_";
		}
		
	}
	close $fh;
	
	# initial pre-processing done, on to bytecode generation:
	
	foreach my $opitem (sort {$a <=> $b} keys %opcodes) {
		my $opcodename = $opcodes{$opitem}{op};
		my @params = @{$opcodes{$opitem}{params}};
		my $opcodenum = $opcodetable{$opcodename}{baseop};
		if(exists $opcodetable{$opcodename}{generate}) {
			my @bytecode = &{$opcodetable{$opcodename}{generate}}($opcodenum, @params); 
			$opcodes{$opitem}{bytecode} = \@bytecode;
		} else {
			die "opcode $opcodename not yet implemented";
		}
	}
	
	# output
	#
	my $cstring;
	my $retarrayref = ();
	foreach my $opitem (sort {$a <=> $b} keys %opcodes) {
		printf "pc: %x ", $opitem;
		print "opcode: $opcodes{$opitem}{op} \tparams: " . join(" ", @{$opcodes{$opitem}{params}}) . "\n";
		my @bytecode = @{$opcodes{$opitem}{bytecode}};
		print "\t";
		foreach my $code (@bytecode) {
			printf "0x%02x, ", $code;
			#$cstring .= sprintf "\\x%02x", $code;
			$cstring .= sprintf ("0x%02x,", $code);
			push(@{$retarrayref}, $code);
		}
		print "\n";
	}
	
	print "$cstring\n";
	my $size = $#{$retarrayref} + 1;
	unshift(@{$retarrayref}, $size);

	return $retarrayref;
}

